/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package provider.impl.genomemap.data;

import commons.provider.ProviderException;
import commons.provider.impl.BaseXMLFileBasedProviderImpl;
import genomemap.data.KSA00Data;
import genomemap.model.KSA00;
import genomemap.provider.Providers;
import org.dom4j.Element;

import java.util.*;

/**
 * @author Susanta Tewari
 * @since Jul 30, 2011
 */
public class KSA00DataProviderImplXML extends BaseXMLFileBasedProviderImpl<KSA00Data>
        implements Providers.KSA00DataProviderFileBased {

    /**
     * Method description
     *
     *
     * @param root
     *
     * @return
     *
     * @throws ProviderException
     */
    @Override
    protected KSA00Data toValue(final Element root) throws ProviderException {


        // 4. get 'probe' elements from the root
        @SuppressWarnings("unchecked") final List<Element> clones = root.elements("clone");


        // 5. count the # of clones
        final int clonesCount = clones.get(0).elementText("sequence").length();


        // 6. prepare data for GtArrayK69Data
        // 6.2 holder of probe sequence for each probe label
        final Map<String, String> probeScoring = new HashMap<>();


        // 7. get elements 'label' and 'sequence'  from the 'probe' element
        for (final Element clone : clones) {

            final String clone_label = clone.attributeValue("label");
            final String sequence    = clone.elementText("sequence");


            // 5.1 check the invariant: all sequences have the same number of sites
            if (sequence.length() != clonesCount) {
                throw new ProviderException("probes count does not match for clone " + clone_label);
            }

            probeScoring.put(clone_label, sequence);
        }


        // 6.4 check for element 'genes' in the root element
        // 6.4.1.1 get the mutation labels
        final List<String> probeLabels =
            Arrays.asList(root.elementText("probes").trim().split("-"));


        // 6.4.1.2 check the invariant: # of mutation labels == sites count
        if (probeLabels.size() != clonesCount) {

            throw new ProviderException(
                "Number of clone labels does not match the number of clones");
        }


        // probe spacings
        final List<Double> probeSpacings = new ArrayList<>();

        for (final String prob : root.elementText("probe-spacings").trim().split("-")) {

            try {

                final double val = Double.parseDouble(prob);

                probeSpacings.add(val);

            } catch (NumberFormatException ex) {
                throw new ProviderException("error parsing probe-spacings", ex);
            }
        }


        // check the invariance on the number of probe spacings and the number of probes
        if (probeLabels.size() + 1 != probeSpacings.size()) {

            throw new ProviderException(
                "invariance failed: probeSpacing.size() == probes.size() + 1");
        }


        // check invariance: # of clones >= # of probes
        if (probeLabels.size() > probeScoring.keySet().size()) {
            throw new ProviderException("invariance failed: # of clones >= # of probes");
        }


        // get ch length
        final int chLength     = Integer.parseInt(root.elementText("chromosome-length"));
        final int clone_length = Integer.parseInt(root.elementText("clone-length"));
        final double false_postive_prob =
            Double.parseDouble(root.elementText("false-positive-prob"));
        final double false_negative_prob =
            Double.parseDouble(root.elementText("false-negative-prob"));


        // create the data
        try {

            return new KSA00Data(new KSA00(probeLabels, probeSpacings, chLength, clone_length,
                                           false_postive_prob, false_negative_prob), probeScoring);

        } catch (IllegalArgumentException e) {
            throw new ProviderException("Data integrity error", e);
        }
    }

    /**
     * Method description
     *
     *
     * @return
     */
    @Override
    public Class<KSA00> getModelClass() {
        return KSA00.class;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    @Override
    public Class<KSA00Data> getDataClass() {
        return KSA00Data.class;
    }
}
